// 定义包路径
package com.lxx.jmb2c.utils;

// 导入相关类

import com.lxx.jmb2c.config.JwtConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * JWT工具类
 * 
 * 这个类封装了JSON Web Token的生成、解析和验证功能
 * 
 * 主要功能：
 * 1. 生成JWT令牌(包含自定义claims)
 * 2. 解析JWT令牌并验证签名
 * 3. 检查令牌过期状态
 * 4. 生成刷新令牌
 * 
 * 安全注意事项：
 * 1. 密钥必须足够复杂(建议至少256位)
 * 2. 令牌过期时间不宜过长(建议1-24小时)
 * 3. 敏感操作需要额外验证
 * 4. 令牌应该通过HTTPS传输
 * 
 * 最佳实践：
 * 1. 使用强随机密钥
 * 2. 设置合理的过期时间
 * 3. 实现令牌刷新机制
 * 4. 记录令牌使用日志
 * 
 * 使用示例：
 * // 生成令牌
 * HashMap<String,Object> claims = new HashMap<>();
 * claims.put("id", 123L);
 * claims.put("role", "admin");
 * String token = jwtUtil.generateToken(claims);
 * 
 * // 解析令牌
 * Claims parsedClaims = jwtUtil.parseToken(token);
 * Long userId = parsedClaims.get("id", Long.class);
 * 
 * // 验证令牌
 * String result = jwtUtil.validateToken(token);
 * if("Valid".equals(result)) {
 *     // 令牌有效
 * }
 * 
 * @author lxx
 * @since 2025-07-02
 */
@Component
public class JwtUtil {

    // 注入JWT配置信息
    @Resource
    private JwtConfig jwtConfig;

    /**
     * 根据Map生成JWT令牌
     * 将Map中的数据转换为JWT Claims后生成令牌
     *
     * @param map 包含需要存储在令牌中的数据的Map
     * @return 生成的JWT字符串
     */
    public String generateToken(HashMap<String,Object> map){
        // 创建Claims对象
        Claims claims=Jwts.claims();
        // 将Map中的所有数据添加到Claims中
        claims.putAll(map);
        // 调用重载方法生成令牌
        return generateToken(claims);
    }

    /**
     * 根据Claims生成JWT令牌
     * 设置令牌的Claims、签发时间、过期时间和签名
     *
     * @param claims JWT的Claims部分，包含需要存储的数据
     * @return 生成的JWT字符串
     */
    public String generateToken(Claims claims){
        return Jwts.builder()
                // 设置Claims（数据部分）
                .setClaims(claims)
                // 设置签发时间
                .setIssuedAt(new Date())
                // 设置过期时间
                .setExpiration(new Date(System.currentTimeMillis() + jwtConfig.getTtl()))
                // 使用配置的密钥进行签名
                .signWith(Keys.hmacShaKeyFor(jwtConfig.getKey().getBytes(StandardCharsets.UTF_8)))
                // 生成最终的JWT字符串
                .compact();
    }

    /**
     * 解析JWT令牌
     * 验证令牌的签名并提取其中的Claims数据
     *
     * @param token JWT字符串
     * @return 令牌中的Claims数据
     */
    public Claims parseToken(String token){
        return Jwts.parserBuilder()
                // 设置用于验证签名的密钥
                .setSigningKey(Keys.hmacShaKeyFor(jwtConfig.getKey().getBytes(StandardCharsets.UTF_8)))
                .build()
                // 解析JWT字符串
                .parseClaimsJws(token)
                // 获取Claims部分
                .getBody();
    }

    /**
     * 校验JWT令牌的有效性
     * 检查令牌是否合法、是否过期、签名是否正确等
     *
     * @param token 要校验的JWT字符串
     * @return 校验结果，可能的值：
     *         - Valid：合法
     *         - Expired：过期
     *         - Invalid：无效
     *         - Malformed：结构不符合标准
     *         - Invalid Signature：签名无效
     */
    public String validateToken(String token) {
        try {
            // 尝试解析令牌
            Jwts.parserBuilder()
                    .setSigningKey(Keys.hmacShaKeyFor(jwtConfig.getKey().getBytes(StandardCharsets.UTF_8)))
                    .build()
                    .parseClaimsJws(token);
            return "Valid";
        } catch (ExpiredJwtException e) {
            return "Expired";
        } catch (SignatureException e) {
            return "Invalid Signature";
        } catch (MalformedJwtException e) {
            return "Malformed";
        } catch (Exception e) {
            return "Invalid";
        }
    }

    /**
     * 检查令牌是否过期
     * 解析令牌并比较其过期时间与当前时间
     *
     * @param token JWT字符串
     * @return true表示已过期，false表示未过期
     */
    public boolean isTokenExpired(String token) {
        try {
            // 解析令牌获取Claims
            Claims claims = parseToken(token);
            // 获取过期时间
            Date expiration = claims.getExpiration();
            // 比较过期时间与当前时间
            return expiration.before(new Date());
        } catch (Exception e) {
            // 解析失败时视为已过期
            return true;
        }
    }

    /**
     * 生成刷新令牌
     * 使用UUID生成一个随机的刷新令牌
     *
     * @return 生成的刷新令牌字符串
     */
    public String generateRefreshToken() {
        return UUID.randomUUID().toString();
    }
}